import {
  PauseCircleOutlined,
  PlayCircleOutlined,
  SoundOutlined,
  StepBackwardOutlined,
  StepForwardOutlined,
  UnorderedListOutlined,
} from '@ant-design/icons';
import {
  Divider,
  Flex,
  List,
  Popover,
  Slider,
  Space,
  Spin,
  Typography,
} from 'antd';
import { Howl } from 'howler';
import React, { useEffect, useRef, useState } from 'react';
import Marquee from 'react-fast-marquee';
import InfiniteScroll from 'react-infinite-scroll-component';
import SiriWave from 'siriwave';

import Loading from '@/components/Loading';
import usePrevious from '@/hooks/ahooks/State/usePrevious';
import {
  raveMp3,
  raveWebm,
  runMp3,
  runWebm,
  vibeMp3,
  vibeWebm,
} from '../../audios';
import styles from './index.less';

const { Text } = Typography;

const audioSrc = (fileName: any) => {
  switch (fileName) {
    case 'ccc':
      return [raveWebm, raveMp3];
    case 'qqq':
      return [vibeWebm, vibeMp3];
    case 'yyy':
      return [runWebm, runMp3];
    default:
      return [];
  }
};

const formatTime = (secs: any) => {
  let minutes = Math.floor(secs / 60) || 0;
  let seconds = secs - minutes * 60 || 0;

  return minutes + ':' + (seconds < 10 ? '0' : '') + seconds;
};

const AudioPlayPage = () => {
  const [playIndex, setPlayIndex] = useState<number>(0);
  const prePlayIndex = usePrevious(playIndex);
  const [playList, setPlayList] = useState<any[]>([]);
  const [playListOpen, setPlayListOpen] = useState(false);
  const [volumeOpen, setVolumeOpen] = useState(false);
  const [buttonType, setButtonType] = useState('play');
  const [processType, setProcessType] = useState('bar');
  const [timer, setTimer] = useState('0:00');
  const [duration, setDuration] = useState('0:00');
  const waveformRef = useRef<HTMLDivElement>(null);
  const siriWaveRef = useRef<SiriWave>();
  const progressRef = useRef<HTMLDivElement>(null);
  const sliderBtnRef = useRef<HTMLDivElement>(null);
  const bodyRef = useRef<HTMLDivElement>(null);
  const howlListRef = useRef<Howl[]>([]);
  const [loading, setLoading] = useState(false);

  const loadMoreData = () => {
    if (loading) {
      return;
    }
    setLoading(true);

    new Promise((resolve) => {
      setTimeout(() => {
        resolve(['ccc', 'qqq', 'yyy', 'ccc', 'qqq', 'yyy']);
      });
    })
      .then((body) => {
        const newData = body as unknown as any[];
        setPlayList([...playList, ...newData]);
        setLoading(false);
      })
      .catch(() => {
        setLoading(false);
      });
  };

  useEffect(() => {
    loadMoreData();
  }, []);

  // 更新波浪动画的高度
  const onResize = () => {
    if (bodyRef.current && siriWaveRef.current) {
      const containerWidth = bodyRef.current.clientWidth;
      const containerHeight = bodyRef.current.clientHeight * 0.3;
      siriWaveRef.current.opt.container.style.top = '12px';
      siriWaveRef.current.opt.container.style.height = `${containerHeight}px`;
      // 更新滑块的位置
      const sound = howlListRef.current[playIndex];
      if (sound) {
        let vol = sound.volume();
        let barWidth = vol * 0.9;
        if (sliderBtnRef.current) {
          sliderBtnRef.current.style.left =
            containerWidth * barWidth + containerWidth * 0.05 - 25 + 'px';
        }
      }
    }
  };

  useEffect(() => {
    if (bodyRef.current) {
      const containerWidth = bodyRef.current.clientWidth;
      const containerHeight = bodyRef.current.clientHeight;
      if (waveformRef.current || document.getElementById('waveform')) {
        const siriWave = new SiriWave({
          container:
            waveformRef.current ||
            (document.getElementById('waveform') as HTMLElement),
          width: containerWidth,
          height: containerHeight * 0.3,
          cover: true,
          speed: 0.03,
          amplitude: 0.7,
          frequency: 2,
        });
        siriWaveRef.current = siriWave;
        siriWave.start();
      }
      window.addEventListener('resize', onResize);
      onResize();
    }
  }, []);

  /**
   * requestAnimationFrame 中调用的更新播放位置的步骤
   */
  const onStep = (index: number) => {
    const sound = howlListRef.current[index];
    const seek = sound.seek() || 0;
    const timerTime = formatTime(Math.round(seek));
    setTimer(timerTime);
    if (progressRef.current)
      progressRef.current.style.width =
        ((seek / sound.duration()) * 100 || 0) + '%';
    // 如果声音仍在播放，请继续前进
    if (sound.playing()) requestAnimationFrame(onStep.bind(null, index));
  };

  /**
   * 根据播放列表索引跳到特定曲目
   * @param {Number} index 播放列表中的索引
   */
  const onSkipTo = (index: number) => {
    const indexValue = prePlayIndex ? prePlayIndex : playIndex;
    const sound = howlListRef.current[indexValue];
    // 停止当前轨迹
    if (sound) {
      sound.stop();
    }
    // 重置进度
    if (progressRef.current) progressRef.current.style.width = '0%';
    // 播放新曲目
    // eslint-disable-next-line
    onPlay(index);
  };

  /**
   * 跳到下一首或上一首曲目
   * @param {String} direction 'next' or 'prev'
   */
  const onSkip = (direction: 'next' | 'prev', indexValue: number) => {
    // 根据轨迹的方向获取下一个轨迹
    let index = 0;
    if (direction === 'prev') {
      index = indexValue - 1;
      if (index < 0) {
        index = playList?.length - 1;
      }
    } else {
      index = indexValue + 1;
      if (index >= playList?.length) {
        index = 0;
      }
    }
    onSkipTo(index);
  };

  /**
   * 播放播放列表的中的歌曲
   * @param {Number} index 播放列表中歌曲的索引（留空可播放第一首或当前歌曲）
   */
  const onPlay = (index?: number) => {
    const indexValue = typeof index === 'number' ? index : playIndex;
    const localSound = howlListRef.current[indexValue];
    const playKey = playList[indexValue];
    const sound = localSound
      ? localSound
      : new Howl({
        src: audioSrc(playKey),
        html5: true, // 强制使用 HTML5 以便音频可以流入 （最适合大文件）
        onplay: function () {
          // 显示持续时间
          const durationTime = formatTime(Math.round(sound.duration()));
          setDuration(durationTime);
          // 开始更新轨道进度
          requestAnimationFrame(onStep.bind(null, indexValue));
          // 如果已经加载，则启动波浪动画
          setProcessType('waveform');
          setButtonType('pause');
        },
        onload: function () {
          // 开始波浪动画
          setProcessType('waveform');
          setButtonType('play');
        },
        onend: function () {
          // 停止波浪动画
          setProcessType('bar');
          onSkip('next', indexValue);
        },
        onpause: function () {
          // 停止波浪动画
          setProcessType('bar');
        },
        onstop: function () {
          // 停止波浪动画
          setProcessType('bar');
        },
        onseek: function () {
          // 开始更新轨道的进度
          requestAnimationFrame(onStep.bind(null, indexValue));
        },
      });
    if (!localSound) {
      howlListRef.current[indexValue] = sound;
    }
    // 开始播放声音
    sound.play();
    // 显示暂停按钮
    const status = sound.state();
    switch (status) {
      case 'loaded':
        setButtonType('pause');
        break;
      case 'unloaded':
      case 'loading':
      default:
        setButtonType('loading');
        break;
    }
    setPlayIndex(indexValue);
  };

  /**
   * 暂停当前播放的曲目
   */
  const onPause = () => {
    const sound = howlListRef.current[playIndex];
    // 把声音关掉
    sound.pause();
    // 显示播放按钮
    setButtonType('play');
  };

  /**
   * 设置音量并更新音量滑块显示
   * @param {Number} val 音量在 0 和 1 之间
   */
  const onVolume = (val: any) => {
    Howler.volume(val);
  };

  /**
   * 在当前播放曲目中定位到新位置
   * @param {Number} pre 跳过歌曲的百分比
   */
  const onSeek = (event: React.MouseEvent) => {
    const containerWidth = bodyRef.current?.clientWidth as number;
    const per = event.clientX / containerWidth;
    const sound = howlListRef.current[playIndex];
    // 将百分比转换为寻找位置
    if (sound.playing()) sound.seek(sound.duration() * per);
  };

  const onTogglePlaylist = (open: boolean) => {
    setPlayListOpen(open);
  };

  const onToggleVolumn = (open: boolean) => {
    setVolumeOpen(open);
  };

  const onChangeComplete = (value: number | number[]) => {
    setVolumeOpen(false);
    const per = (value as number) / 100;
    onVolume(per);
  };

  const onChangePlayList = (index: number) => {
    setPlayListOpen(false);
    setPlayIndex(index);
    onSkipTo(index);
  };

  return (
    <div style={{ height: 800 }} ref={bodyRef}>
      <div className={styles['body']} id="body">
        <div className={styles['title']}>
          <div className={styles['timer']}>{timer}</div>
          <div className={styles['track']}>
            <Marquee pauseOnHover gradient={false}>
              <Space size={2}>
                <Text strong className={styles['text']}>
                  {playIndex}.
                </Text>
                <Text className={styles['text']}>10月13日 12:12:20</Text>
                <Text className={styles['text']}>超越</Text>
              </Space>
            </Marquee>
          </div>
          <div className={styles['duration']}>{duration}</div>
        </div>
        <div className={styles['controls']}>
          <div className={styles['playListBtn']}>
            <Popover
              open={playListOpen}
              onOpenChange={onTogglePlaylist}
              getPopupContainer={() =>
                document.getElementById('body') || document.body
              }
              overlayClassName={styles['scrollablePopover']}
              overlayStyle={{}}
              overlayInnerStyle={{
                padding: 0,
              }}
              content={
                <div
                  id="scrollableContainer"
                  className={styles['scrollableContainer']}
                >
                  <InfiniteScroll
                    dataLength={playList.length}
                    next={loadMoreData}
                    hasMore={true}
                    loader={
                      <Flex justify="center">
                        <Spin size="small" />
                      </Flex>
                    }
                    endMessage={<Divider plain>已经展示全部</Divider>}
                    scrollableTarget={'scrollableContainer'}
                    scrollThreshold={0.8}
                    className={styles['infiniteScroll']}
                    style={{
                      overflow: 'visible',
                    }}
                  >
                    <List
                      itemLayout="horizontal"
                      split
                      bordered={false}
                      dataSource={playList}
                      renderItem={(item, index) => (
                        <List.Item
                          key={`${item}-${index}`}
                          className={styles['listItem']}
                          onClick={onChangePlayList.bind(null, index)}
                        >
                          <Space>
                            <Text strong className={styles['text']}>
                              {index}.
                            </Text>
                            <Text>10月13日 12:12:20</Text>
                            <Text strong>{item}</Text>
                          </Space>
                        </List.Item>
                      )}
                    />
                  </InfiniteScroll>
                </div>
              }
              trigger="click"
            >
              <UnorderedListOutlined
                className={`${styles['icon']} ${styles['hover']}`}
              />
            </Popover>
          </div>
          <div className={styles['operate']}>
            <StepBackwardOutlined
              className={`${styles['icon']} ${styles['hover']}`}
              onClick={() => onSkip('prev', playIndex)}
            />
            {buttonType === 'loading' && <Loading />}
            {buttonType === 'play' && (
              <PlayCircleOutlined
                className={`${styles['icon']} ${styles['hover']}`}
                onClick={() => onPlay()}
              />
            )}
            {buttonType === 'pause' && (
              <PauseCircleOutlined
                className={`${styles['icon']} ${styles['hover']}`}
                onClick={onPause}
              />
            )}
            <StepForwardOutlined
              className={`${styles['icon']} ${styles['hover']}`}
              onClick={() => onSkip('next', playIndex)}
            />
          </div>
          <div className={`${styles['volumeBtn']} ${styles['hover']}`}>
            <Popover
              arrow
              open={volumeOpen}
              onOpenChange={onToggleVolumn}
              placement="top"
              content={
                <Slider
                  vertical
                  defaultValue={100}
                  styles={{
                    rail: {
                      backgroundColor: '#bfbfbf',
                    },
                  }}
                  style={{
                    height: 80,
                  }}
                  min={0}
                  max={100}
                  onChangeComplete={onChangeComplete}
                />
              }
              overlayInnerStyle={{
                paddingBlock: 8,
                paddingInline: 0,
              }}
            >
              <SoundOutlined className={styles['icon']} />
            </Popover>
          </div>
        </div>
        <div
          id="waveform"
          className={styles['waveform']}
          ref={waveformRef}
          style={{
            display: processType === 'waveform' ? 'block' : 'none',
          }}
          onClick={onSeek}
        />
        <div
          id="bar"
          className={styles['bar']}
          style={{
            display: processType === 'bar' ? 'block' : 'none',
          }}
        />
        <div id="progress" className={styles['progress']} ref={progressRef} />
      </div>
    </div>
  );
};

export default AudioPlayPage;
